package com.gzx.plugin.tjzy.core.utils.crypter;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.RecordComponent;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * Object 工具类
 */
@Slf4j
public final class ObjectUtil {
    private ObjectUtil() {
        // Util class
    }
    /**
     * 如果参数为null，则转为 {@link ObjectUtil#NULL}
     */
    public static Object nullToNullValue(Object object) {
        if (object == null) {
            return NULL;
        }
        return object;
    }




    /**
     * 如果参数为 {@link ObjectUtil#NULL}，则转为 null
     */
    public static Object nullValueToNull(Object object) {
        if (NULL.equals(object)) {
            return null;
        }
        return object;
    }

    /**
     * null 占位对象
     * 当需要表达null的概念，而又不能使用 null 时，可使用此对象
     */
    public static final Object NULL = new Object();

    /**
     * 返回自己
     */
    public static  <T> T self(T object) {
        return object;
    }

    public static boolean isEmpty(String object) {
        return object == null || object.isEmpty();
    }

    public static boolean isEmpty(Collection<?> object) {
        return object == null || object.isEmpty();
    }

    public static boolean notEmpty(String object) {
        return !isEmpty(object);
    }

    public static boolean notEmpty(Collection<?> object) {
        return !isEmpty(object);
    }

    /**
     * 获取第一个不为 null 的值，如果都为空则返回 null
     */
    public static <T> T firstNonNull(T... objects) {
        for (T object : objects) {
            if (object != null) {
                return object;
            }
        }
        return null;
    }

    /**
     * 如果值不为空则返回此值，否则返回 supplier 生成的值
     */
    public static <T> T notNullOr(T object, Supplier<? extends T> supplier) {
        if (object == null) {
            return supplier.get();
        }
        return object;
    }

    /**
     * 如果值不为空则返回此值，否则返回 other
     */
    public static <T> T notNullOr(T object, T other) {
        if (object == null) {
            return other;
        }
        return object;
    }

    /**
     * 如果值不为空则返回 function 计算出的值，否则返回 null
     */
    public static <T, R> R notNullAnd(T object, Function<T, R> function) {
        if (object == null) {
            return null;
        }
        return function.apply(object);
    }

    /**
     * 如果 {@code predicate.test(object) == true} 则执行 {@code consumer.accept(object)}
     */
    public static <T> void ifThen(T object, Predicate <T> predicate, Consumer<T> consumer) {
        if (predicate.test(object)) {
            consumer.accept(object);
        }
    }

    /**
     * 如果值不为空则执行指定方法
     */
    public static <T> void notNullThen(T object, Consumer<T> consumer) {
        ifThen(object, Objects::nonNull, consumer);
    }

    /**
     * 如果值不为空则执行指定方法
     */
    public static void notEmptyThen(String object, Consumer<String> consumer) {
        ifThen(object, ObjectUtil::notEmpty, consumer);
    }

    /**
     * 如果值不为空则执行指定方法
     */
    public static <T extends Collection> void notEmptyThen(T object, Consumer<T> consumer) {
        ifThen(object, ObjectUtil::notEmpty, consumer);
    }

    /**
     * 复制一个 {@link Record}，
     * 将使用 newValueMap 中的值覆盖 src 中对应的属性值
     * @param src 原值
     * @param newValueMap 新属性值映射
     */
    @SuppressWarnings("unchecked")
    public static <T extends Record> T copyRecord(T src, Map <String, Object> newValueMap) {
        Class<? extends Record> cls = src.getClass();
        RecordComponent[] recordComponents = cls.getRecordComponents();
        Class<?>[] paramTypes = Arrays.stream(recordComponents).map(RecordComponent::getType).toArray(Class<?>[]::new);
        try {
            Constructor <? extends Record> constructor = cls.getDeclaredConstructor(paramTypes);
            Object[] initArgs = Arrays.stream(recordComponents).map(component -> {
                String name = component.getName();
                if (newValueMap.containsKey(name)) {
                    Object value = newValueMap.get(name);
                    if (value == NULL) {
                        return null;
                    } else {
                        return value;
                    }
                } else {
                    try {
                        return component.getAccessor().invoke(src);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        throw new IllegalStateException(e);
                    }
                }
            }).toArray(Object[]::new);
            return (T) constructor.newInstance(initArgs);
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }


}
